#include <iostream>
#include <thread>
#include <mutex>
#include <ctime>
#include <array>

// modify the number of philosophers
#define PHILOSOPHERS 10

using namespace std;
using namespace std::this_thread;
using namespace std::chrono;

mutex chopstick[PHILOSOPHERS];

int meal_time[PHILOSOPHERS];

mutex waiter;

// initialize the meal time
void init()
{
	for (int i = 0; i < PHILOSOPHERS; ++i)
		meal_time[i] = 50;
}

// ask waiter for service
bool ask_waiter(int id){
	bool left_state, right_state;

	if(left_state = chopstick[id].try_lock()){
		chopstick[id].unlock();
	}

	if(right_state = chopstick[(id+1) % PHILOSOPHERS].try_lock()){
		chopstick[(id+1) % PHILOSOPHERS].unlock();
	}

	if(left_state && right_state){
		return true;
	}
	else{
		return false;
	}
	
}

void eat(int id)
{
	while (true)
	{
		// Get waiter's service lock
		waiter.lock();
		cout << "Philosopher " << id + 1 << " get waiter's service ..." << endl;

		if(ask_waiter(id)){
			// try to get the first chopstick
			chopstick[id].lock();
			cout << "Philosopher " << id + 1 << " get left chopstick ..." << endl;
			sleep_for(milliseconds(1));
			// try to get the second chopstick
			chopstick[(id + 1) % PHILOSOPHERS].lock();
			cout << "Philosopher " << id + 1 << " get right chopstick ..." << endl;

			// unlock waiter's service
			waiter.unlock();
		}
		else
		{
			// unlock waiter's service
			waiter.unlock();
			continue;
		}

		cout << "Philosopher " << id + 1 << " begin eating ..." << endl;

		int eated_time = rand() % (30 + 1);

		// eating ...
		sleep_for(milliseconds(eated_time % 10));

		meal_time[id] -= eated_time;

		chopstick[id].unlock();
		chopstick[(id + 1) % PHILOSOPHERS].unlock();

		// finished eating
		if (meal_time[id] <= 0)
		{
			cout << "Philosopher " << id + 1 << " finished." << endl;
			break;
		}
		else
		{
			cout << "Philosopher " << id + 1 << " still need " << meal_time[id] << " minues to eat." << endl;
		}
		// thinking ...
		sleep_for(milliseconds(200));
	}
}

int main()
{
	//initialize each Philosopher's meal time.
	init();

	array<thread, PHILOSOPHERS> threads;

	cout << "Starting..." << endl;
	for (int i = 0; i < PHILOSOPHERS; ++i)
		threads[i] = thread(eat, i);

	for (int i = 0; i < PHILOSOPHERS; ++i)
		threads[i].join();

	printf("All philosophers done.\n");
	return 0;
}